﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using InterfazGrafico.LibreriaClases.Clases;
using InterfazGrafico.LibreriaConstantes;
using InterfazGrafico.LibreriaConstantes.Elementos;

namespace InterfazGrafico.LibreriaClases.Propiedades
{
    /// <summary>
    /// Tipos posibles de los nodos de definicion de datos.
    /// </summary>
    public enum TiposNodosDefinicionDatos
    {
        Nulo,
        Valor,
        Enlace,
        Mascara
    };

    /// <summary>
    /// Clase de conversión usada para convertir la cadena dada a un tipo de nodo definición de datos.
    /// </summary>
    public static class ConversorTiposNodosDefinicionDatos
    {
        /// <summary>
        /// Convierte la cadena proporcionada en un tipo de nodo de definición de datos válido.
        /// </summary>
        /// <param name="tipo">Cadena con el tipo de nodo</param>
        /// <returns>Tipo de nodo de definición de datos</returns>
        public static TiposNodosDefinicionDatos Convertir(string tipo)
        {
            switch (tipo)
            {
                case "Valor":
                    return TiposNodosDefinicionDatos.Valor;
                case "Enlace":
                    return TiposNodosDefinicionDatos.Enlace;
                case "Mascara":
                    return TiposNodosDefinicionDatos.Mascara;
            }
            return TiposNodosDefinicionDatos.Nulo;
        }

        /// <summary>
        /// Convierte el tipo de nodo de definición de datos proporcionado en una cadena de caracteres.
        /// </summary>
        /// <param name="tipo">Tipo de nodo de definición de datos</param>
        /// <returns>Cadena de caracteres asociada</returns>
        public static string Convertir(TiposNodosDefinicionDatos tipo)
        {
            switch (tipo)
            {
                case TiposNodosDefinicionDatos.Valor:
                    return "Valor";
                case TiposNodosDefinicionDatos.Enlace:
                    return "Enlace";
                case TiposNodosDefinicionDatos.Mascara:
                    return "Mascara";
            }
            return string.Empty;
        }
    }

    /*********************************************************/
    /* Clases auxiliares                                     */
    /*********************************************************/
    /// <summary>
    /// Clase que contiene los datos del diccionario de datos.
    /// </summary>
    public class Valor
    {
        /// <summary>
        /// Indica si el dato asociado apunta a un enlace
        /// </summary>
        private bool _esEnlace = false;

        /// <summary>
        /// Obtiene si el dato asociado apunta a un enlace
        /// </summary>
        public bool EsEnlace
        {
            get
            {
                return _esEnlace;
            }
        }

        /// <summary>
        /// Contiene el enlace destino asociado al dato
        /// </summary>
        private string _enlaceA = string.Empty;

        /// <summary>
        /// Obtiene el enlace destino asociado al dato
        /// </summary>
        public string EnlaceA
        {
            get
            {
                return _enlaceA;
            }
        }

        /// <summary>
        /// Tag asociado al dato.
        /// </summary>
        private string _tag = string.Empty;

        /// <summary>
        /// Obtiene el tag asociado al dato
        /// </summary>
        public string Tag
        {
            get
            {
                return _tag;
            }
        }

        /// <summary>
        /// Contiene la asignación a otros tags
        /// </summary>
        private Dictionary<string, string> _otrosTags = null;

        /// <summary>
        /// Obtiene o establece la asignación a otros tags
        /// </summary>
        public Dictionary<string, string> OtrosTags
        {
            get
            {
                if (_otrosTags == null)
                    _otrosTags = new Dictionary<string, string>();
                return _otrosTags;
            }
            set
            {
                _otrosTags = value;
            }
        }

        /// <summary>
        /// Contiene el valor a escribir a la salida.
        /// </summary>
        private string _salidaModificada = string.Empty;

        /// <summary>
        /// Contiene el valor a escribir a la salida
        /// </summary>
        public string SalidaModificada
        {
            get
            {
                return _salidaModificada;
            }
        }

        /// <summary>
        /// Constructor.
        /// Asigna el tipo del valor como enlace.
        /// </summary>
        /// <param name="enlace">Enlace al que apunta el dato</param>
        public Valor(string enlace)
        {
            this._esEnlace = true;
            this._enlaceA = enlace;
        }

        /// <summary>
        /// Constructor.
        /// Asigna el tipo del valor como dato.
        /// </summary>
        /// <param name="tagAsociado">Tag asociado al dato</param>
        /// <param name="salidaModificada">Salida que se escribirá en la salida</param>
        public Valor(string tagAsociado, string salidaModificada)
        {
            this._esEnlace = false;
            this._tag = tagAsociado;
            this._salidaModificada = salidaModificada;
        }

        /// <summary>
        /// Asigna valores a otros tags del dato.
        /// </summary>
        /// <param name="tag">Nuevo tag a incluir</param>
        /// <param name="valor">Valor del tag</param>
        public void AsignarOtrosTags(string tag, string valor)
        {
            try
            {
                this.OtrosTags.Add(tag, valor);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

    /// <summary>
    /// Clase que contiene la máscara a usar para transformar un dato de entrada.
    /// </summary>
    public class Mascara
    {
        /// <summary>
        /// Máscara del dato de salida a obtener.
        /// </summary>
        private string Salida = string.Empty;

        /// <summary>
        /// Obtiene la máscara del dato de salida a obtener.
        /// </summary>
        internal string SalidaModificada
        {
            get
            {
                return Salida;
            }
        }

        /// <summary>
        /// Tag asociado al dato.
        /// </summary>
        private string _tag = string.Empty;

        /// <summary>
        /// Obtiene el tag asociado al dato
        /// </summary>
        public string Tag
        {
            get
            {
                return _tag;
            }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="salida">Máscara de salida asociada a la máscara de entrada</param>
        /// <param name="tagAsociado">Tag asociado al dato</param>
        public Mascara(string salida, string tagAsociado)
        {
            this.Salida = salida;
            this._tag = tagAsociado;
        }

        /// <summary>
        /// Obtiene la máscara de un dato.
        /// </summary>
        /// <param name="dato">Dato del que obtener la máscara</param>
        /// <returns>Máscara del dato</returns>
        public static string obtenerMascara(string dato)
        {
            string mascara = string.Empty;
            for (int i = 0; i < dato.Length; i++)
            {
                if (Char.IsNumber(dato, i))
                    mascara = mascara + "N";
                else if (Char.IsLetter(dato, i))
                    mascara = mascara + "A";
                else
                    mascara = mascara + dato[i];
            }
            return mascara;
        }

        /// <summary>
        /// Obtiene un dato transformado en base a máscaras.
        /// </summary>
        /// <param name="entrada">Dato de entrada</param>
        /// <param name="mascaraEntrada">Máscara del dato de entrada</param>
        /// <returns>Dato de salida con la máscara correspondiente</returns>
        public string obtenerDatoSalida(string entrada, string mascaraEntrada)
        {
            if (!string.Empty.Equals(Salida))
                return modificarMascara(entrada, mascaraEntrada, Salida);
            else
                return entrada;
        }

        /// <summary>
        /// Obtiene un dato transformado en base a máscaras.
        /// </summary>
        /// <param name="dato">Dato de entrada</param>
        /// <param name="mascaraEntrada">Máscara del dato de entrada</param>
        /// <param name="mascaraSalida">Máscara del dato de salida deseado</param>
        /// <returns>Dato de salida con la máscara indicada</returns>
        private static string modificarMascara(string dato, string mascaraEntrada, string mascaraSalida)
        {
            string datoSalida = string.Empty;
            Dictionary<char, int> indicesMascara = new Dictionary<char, int>();
            indicesMascara.Add('A', 0);
            indicesMascara.Add('N', 0);
            for (int i = 0; i < mascaraSalida.Length; i++)
            {
                if (!mascaraSalida[i].Equals('A') && !mascaraSalida[i].Equals('N'))
                    datoSalida += mascaraSalida[i];
                else
                {
                    // Busca dato en la máscara de entrada
                    if (indicesMascara[mascaraSalida[i]] < dato.Length)
                    {
                        while (mascaraEntrada.Length > indicesMascara[mascaraSalida[i]]
                            && !mascaraEntrada[indicesMascara[mascaraSalida[i]]].Equals(mascaraSalida[i]))
                            indicesMascara[mascaraSalida[i]]++;
                        if (mascaraEntrada[indicesMascara[mascaraSalida[i]]].Equals(mascaraSalida[i]))
                        {
                            datoSalida += dato[indicesMascara[mascaraSalida[i]]];
                            indicesMascara[mascaraSalida[i]]++;
                        }
                    }
                }
            }
            return datoSalida;
        }
    }

    /**********************************************************/
    /* Propiedades                                            */
    /**********************************************************/
    /// <summary>
    /// Almacena las propiedades de un elemento específico.
    /// </summary>
    public class PDefinicionDatos : Propiedades
    {
        /// <summary>
        /// Diccionario Valores.
        /// </summary>
        public Dictionary<string, Valor> DiccionarioValores
        {
            get
            {
                if (!this.contienePropiedad(CDefinicionDatos.DiccionarioValores))
                    this.addPropiedad(CDefinicionDatos.DiccionarioValores, new Dictionary<string, Valor>());
                return (Dictionary<string, Valor>)this.getPropiedad(CDefinicionDatos.DiccionarioValores);
            }
            set
            {
                this.setOrAddPropiedad(CDefinicionDatos.DiccionarioValores, value);
            }
        }

        /// <summary>
        /// Diccionario Máscaras.
        /// </summary>
        public Dictionary<string, Mascara> DiccionarioMascaras
        {
            get
            {
                if (!this.contienePropiedad(CDefinicionDatos.DiccionarioMascaras))
                    this.addPropiedad(CDefinicionDatos.DiccionarioMascaras, new Dictionary<string, Mascara>());
                return (Dictionary<string, Mascara>)this.getPropiedad(CDefinicionDatos.DiccionarioMascaras);
            }
            set
            {
                this.setOrAddPropiedad(CDefinicionDatos.DiccionarioMascaras, value);
            }
        }

        /// <summary>
        /// Constructor por defecto.
        /// </summary>
        public PDefinicionDatos()
            : base()
        {
        }

        /// <summary>
        /// Obtiene los parámetros a partir del fichero XML indicado.
        /// </summary>
        /// <param name="fichero">Fichero de configuración XML</param>
        override public void leer(string fichero)
        {
            try
            {
                base.leer(fichero);
                XmlDocument xml = LeerXml.AbrirFicheroParametros(fichero);
                try
                {
                    // Datos
                    int numeroDatos = LeerXml.ObtenerNumeroNodosCampo(xml, CDefinicionDatos.RutaNodoRaiz);
                    this.DiccionarioValores = new Dictionary<string, Valor>();
                    this.DiccionarioMascaras = new Dictionary<string, Mascara>();
                    for (int indiceDato = 0; indiceDato < numeroDatos; indiceDato++)
                    {
                        string tipo = string.Empty;
                        string valor = string.Empty;
                        try
                        { tipo = LeerXml.ObtenerValorCampo(xml, CDefinicionDatos.RutaNodoRaiz, indiceDato, CDefinicionDatos.NodoDatoTipo); }
                        catch (Exception)
                        { tipo = string.Empty; }
                        try
                        { valor = LeerXml.ObtenerValorCampo(xml, CDefinicionDatos.RutaNodoRaiz, indiceDato, CDefinicionDatos.NodoDatoValor); }
                        catch (Exception)
                        { valor = string.Empty; }
                        string salida = string.Empty;
                        try
                        { salida = LeerXml.ObtenerValorCampo(xml, CDefinicionDatos.RutaNodoRaiz, indiceDato, CDefinicionDatos.NodoDatoSalida); }
                        catch (Exception)
                        { salida = string.Empty; }
                        if (!string.Empty.Equals(tipo))
                        {
                            if (TiposNodosDefinicionDatos.Valor.Equals(ConversorTiposNodosDefinicionDatos.Convertir(tipo)))
                            {
                                // Valor
                                string tag = string.Empty;
                                try
                                { tag = LeerXml.ObtenerValorCampo(xml, CDefinicionDatos.RutaNodoRaiz, indiceDato, CDefinicionDatos.NodoDatoTag); }
                                catch (Exception)
                                { tag = string.Empty; }
                                if (!string.Empty.Equals(tag))
                                    if (this.DiccionarioValores.ContainsKey(valor))
                                        this.DiccionarioValores[valor] = new Valor(tag, salida);
                                    else
                                        this.DiccionarioValores.Add(valor, new Valor(tag, salida));
                                // Otros tags
                                this.DiccionarioValores[valor].OtrosTags = new Dictionary<string, string>();
                                int numeroOtrosTags = 0;
                                try
                                { numeroOtrosTags = LeerXml.ObtenerNumeroNodosCampo(xml, CDefinicionDatos.RutaNodoRaiz, indiceDato, CDefinicionDatos.NodoDatoOtrosTags); }
                                catch (Exception)
                                { numeroOtrosTags = 0; }
                                for (int indiceOtros = 0; indiceOtros < numeroOtrosTags; indiceOtros++)
                                {
                                    string alias = string.Empty;
                                    string valorAlias = string.Empty;

                                    try
                                    { alias = LeerXml.ObtenerValorCampo(xml, CDefinicionDatos.RutaNodoRaiz, indiceDato, CDefinicionDatos.NodoDatoOtrosTags, indiceOtros, CDefinicionDatos.NodoDatoOtrosTagsTagAlias); }
                                    catch (Exception)
                                    { alias = string.Empty; }

                                    try
                                    { valorAlias = LeerXml.ObtenerValorCampo(xml, CDefinicionDatos.RutaNodoRaiz, indiceDato, CDefinicionDatos.NodoDatoOtrosTags, indiceOtros, CDefinicionDatos.NodoDatoOtrosTagsTagValor); }
                                    catch (Exception)
                                    { valorAlias = string.Empty; }
                                    if (!string.Empty.Equals(alias))
                                        this.DiccionarioValores[valor].OtrosTags.Add(alias, valorAlias);
                                }
                            }
                            else if (TiposNodosDefinicionDatos.Enlace.Equals(ConversorTiposNodosDefinicionDatos.Convertir(tipo)))
                            {
                                // Enlace
                                if (this.DiccionarioValores.ContainsKey(valor))
                                    this.DiccionarioValores[valor] = new Valor(salida);
                                else
                                    this.DiccionarioValores.Add(valor, new Valor(salida));
                            }
                            else if (TiposNodosDefinicionDatos.Mascara.Equals(ConversorTiposNodosDefinicionDatos.Convertir(tipo)))
                            {
                                // Máscara
                                string tag = string.Empty;
                                try
                                { tag = LeerXml.ObtenerValorCampo(xml, CDefinicionDatos.RutaNodoRaiz, indiceDato, CDefinicionDatos.NodoDatoTag); }
                                catch (Exception)
                                { tag = string.Empty; }
                                if (!string.Empty.Equals(tag))
                                {
                                    if (this.DiccionarioMascaras.ContainsKey(valor))
                                        this.DiccionarioMascaras[valor] = new Mascara(salida, tag);
                                    else
                                        this.DiccionarioMascaras.Add(valor, new Mascara(salida, tag));
                                }
                            }
                        }
                    }
                }
                catch (Exception)
                {
                    this.DiccionarioValores = new Dictionary<string, Valor>();
                    this.DiccionarioMascaras = new Dictionary<string, Mascara>();
                }
            }
            catch (Exception ex)
            {
                throw new ApplicationException(Constantes.ERROR_LECTURA_FICHERO_CONFIGURACION, ex);
            }
        }

        /// <summary>
        /// Escribe los parámetros al fichero XML indicado.
        /// </summary>
        /// <param name="fichero">Fichero de configuración XML</param>
        public override void escribir(string fichero)
        {
            try
            {
                base.escribir(fichero);
                XmlWriter xml = EscribirXml.AbrirFicheroParametros(fichero);
                EscribirXml.InicioElemento(xml, CDefinicionDatos.NodoRaiz);
                // Valores
                foreach (string valor in DiccionarioValores.Keys)
                {
                    EscribirXml.InicioElemento(xml, CDefinicionDatos.NodoDato);
                    if (DiccionarioValores[valor].EsEnlace)
                    {
                        EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoTipo, ConversorTiposNodosDefinicionDatos.Convertir(TiposNodosDefinicionDatos.Enlace));
                        EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoValor, valor);
                        EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoSalida, DiccionarioValores[valor].EnlaceA);
                    }
                    else
                    {
                        EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoTipo, ConversorTiposNodosDefinicionDatos.Convertir(TiposNodosDefinicionDatos.Valor));
                        EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoValor, valor);
                        EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoTag, DiccionarioValores[valor].Tag);
                        EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoSalida, DiccionarioValores[valor].SalidaModificada);
                        EscribirXml.InicioElemento(xml, CDefinicionDatos.NodoDatoOtrosTags);
                        foreach (string alias in DiccionarioValores[valor].OtrosTags.Keys)
                        {
                            EscribirXml.InicioElemento(xml, CDefinicionDatos.NodoDatoOtrosTagsTag);
                            EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoOtrosTagsTagAlias, alias);
                            EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoOtrosTagsTagValor, DiccionarioValores[valor].OtrosTags[alias]);
                            EscribirXml.FinElemento(xml);
                        }
                        EscribirXml.FinElemento(xml);
                    }
                    EscribirXml.FinElemento(xml);
                }
                // Mascaras
                foreach (string mascara in DiccionarioMascaras.Keys)
                {
                    EscribirXml.InicioElemento(xml, CDefinicionDatos.NodoDato);
                    EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoTipo, ConversorTiposNodosDefinicionDatos.Convertir(TiposNodosDefinicionDatos.Mascara));
                    EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoValor, mascara);
                    EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoTag, DiccionarioMascaras[mascara].Tag);
                    EscribirXml.EscribeNodo(xml, CDefinicionDatos.NodoDatoSalida, DiccionarioMascaras[mascara].SalidaModificada);
                }
                EscribirXml.FinElemento(xml);
                EscribirXml.Flush(xml);
                EscribirXml.Close(xml);
            }
            catch (Exception ex)
            {
                throw new ApplicationException(Constantes.ERROR_ESCRITURA_FICHERO_CONFIGURACION, ex);
            }
        }
    }
}

 